#include "StdAfx.h"
#include "TabContainer.h"
#include "RichEditTabView.h"
#include <algorithm>
#include <functional>
#include <windows.h>
#include <commctrl.h>
#include <malloc.h>


using namespace std;
using namespace::std::tr1;
using namespace::std::tr1::placeholders;

LRESULT SubClassForRichEdit(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam);
LRESULT SubClassForTabControl(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam);

WNDPROC lpfnOldWndProcForRichEdit;
WNDPROC lpfnOldWndProcForTabControl;

CTabContainer* CTabContainer::s_pTheTabContainer = NULL;

CTabContainer::CTabContainer(HWND hParent, HINSTANCE hInstance, RECT tabRect) : m_nextItem(0)
{
	if (s_pTheTabContainer != 0)
	{
			throw L"Only one instance of CTabContainer is allowed";
	}
	s_pTheTabContainer = this;

	m_hInstance = hInstance;
	m_tabRect = tabRect;
	
	m_hWnd = CreateWindowEx(
        0,                      // extended style
        WC_TABCONTROL,          // tab control constant
        L"",                     // text/caption
		WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN,  // is a child control, and visible
		m_tabRect.left,                      // X position - device units from left
		m_tabRect.top,                      // Y position - device units from top
		m_tabRect.right - m_tabRect.left,                    // Width - in device units
		m_tabRect.bottom - m_tabRect.top,                    // Height - in device units
        hParent,            // parent window
        NULL,                   // no menu
        hInstance,                 // instance
        NULL                    // no extra junk
        );

    if (m_hWnd == NULL)
    {
		throw L"Error creating TAB control";
	}
#ifdef _WIN64
	lpfnOldWndProcForTabControl = (WNDPROC)SetWindowLongPtrW(m_hWnd, GWLP_WNDPROC, (LONG_PTR)SubClassForTabControl);
#else 
	lpfnOldWndProcForTabControl = (WNDPROC)SetWindowLong(m_hWnd, GWL_WNDPROC, (DWORD)SubClassForTabControl);
#endif
	
}

CTabContainer::~CTabContainer(void)
{
	for_each(m_tabs.begin(), m_tabs.end(), bind(&CTabContainer::DeleteTab, this, _1));
	m_tabs.clear();

	if (m_hWnd != 0)
		DestroyWindow(m_hWnd);
	m_hWnd = 0;
}

void CTabContainer::DeleteTab(shared_ptr<CTabView> tabView)
{
	if (m_currentSelectedTab == tabView && !m_tabs.empty())
		SwitchToTab(m_tabs[0]);
	tabView.reset();
}

RECT CTabContainer::GetTabSize() const
{
	RECT rect;
	TabCtrl_GetItemRect(m_hWnd, 0, &rect);
	
	rect.top = rect.bottom + 2;
	rect.left = m_tabRect.left;
	rect.right = m_tabRect.right;
	rect.bottom = m_tabRect.bottom - m_tabRect.top;

	return rect;
}

void  CTabContainer::SetTabSize(shared_ptr<CTabView> tabView)
{
	if(tabView.get() != NULL)
	{
		tabView->SetSize(GetTabSize());
	}
}

void  CTabContainer::UpdateTabSizes()
{
	for_each(m_tabs.begin(), m_tabs.end(), bind(&CTabContainer::SetTabSize, this, _1));
}

void CTabContainer::UpdateTabSize(RECT r)
{
	m_tabRect = r;
	MoveWindow(m_hWnd, r.left, r.top, r.right - r.left, r.bottom - r.top, TRUE);
	UpdateTabSizes();
}



void CTabContainer::AddTab(shared_ptr<CTabView> tabView)
{
	TCITEM tie;  // tab item structure


	HWND hTab = tabView->CreateTab(m_hWnd, m_hInstance, m_nextItem);
	if (!IsWindow(hTab))
		throw L"Error creating tab"; 

	
#ifdef _WIN64
	lpfnOldWndProcForRichEdit = (WNDPROC)SetWindowLongPtrW(hTab, GWLP_WNDPROC, (LONG_PTR)SubClassForRichEdit);
#else 
	lpfnOldWndProcForRichEdit = (WNDPROC)SetWindowLong(hTab, GWL_WNDPROC, (DWORD)SubClassForRichEdit);
#endif


    /* set up our item structure */
    tie.mask = TCIF_TEXT; 
	const wstring &title = tabView->GetTitle().c_str();
	size_t length = (title.length() + 1) * 2;
	LPWSTR pText = (LPWSTR)_alloca(length);
	memcpy_s(pText, length, title.c_str(), length);

	tie.pszText = pText;  // the tab's text/caption

    if (TabCtrl_InsertItem(m_hWnd, m_nextItem, &tie) == -1)
    {
        /* couldn't insert tab item */
        DestroyWindow(hTab);
		throw L"Couldn't add Tab";
    }
	
	SetTabSize(tabView);

	if (m_tabs.capacity() <= m_nextItem)
		m_tabs.resize(m_tabs.capacity() + 5);
	m_tabs[m_nextItem] = tabView;
	++m_nextItem;

	SwitchToTab(tabView);
}


bool CTabContainer::CheckTabId(shared_ptr<CTabView> tab, const wstring &id) const
{
	return tab->GetId() == id;
}

shared_ptr<CTabView> CTabContainer::GetTabById(const wstring &id)
{
	vector<shared_ptr<CTabView>>::iterator iter = 
		find_if(m_tabs.begin(), m_tabs.end(), bind(&CTabContainer::CheckTabId, this, _1, id));

	if (iter == m_tabs.end())
		return shared_ptr<CTabView>();

	return *iter;
}

void CTabContainer::TabChanged()
{
	int iPage = TabCtrl_GetCurSel(m_hWnd);
	SwitchToTab(m_tabs[iPage]);
}

void CTabContainer::SwitchToTab(shared_ptr<CTabView> tabView)
{
	if (m_currentSelectedTab)
		m_currentSelectedTab->Hide();
	
	TabCtrl_SetCurSel(m_hWnd, tabView->GetIndex());

	tabView->Show();
	m_currentSelectedTab = tabView;
}

void CTabContainer::Copy()
{
	// TODO: Check if m_currentSelectedTab is CRichEditTabView
	CRichEditTabView *richEdit = (CRichEditTabView*)m_currentSelectedTab.get();
	richEdit->GetRichEdit()->Copy();
}
CTabView::~CTabView()
{
	if (m_hWnd)
		DestroyWindow(m_hWnd);
	m_hWnd = 0;
}

void CTabView::Show()
{
	ShowWindow(m_hWnd, SW_SHOW); 
	InvalidateRect(m_hWnd, NULL, TRUE);
}

void CTabView::Hide()
{
	ShowWindow(m_hWnd, SW_HIDE);
}

void CTabView::SetSize(RECT rect)
{
	MoveWindow(m_hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
}


bool ctrl = false;
bool shift = false;
bool tab = false;
LRESULT SubClassForRichEdit(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
	return CTabContainer::s_pTheTabContainer->ProcessMessageForSwitchTab(hWnd, Message, wParam, lParam, lpfnOldWndProcForRichEdit);
}

LRESULT SubClassForTabControl(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{	
	return CTabContainer::s_pTheTabContainer->ProcessMessageForSwitchTab(hWnd, Message, wParam, lParam, lpfnOldWndProcForTabControl);
}

LRESULT CTabContainer::ProcessMessageForSwitchTab(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam, WNDPROC lpfnOldWndProc)
{
	switch(Message)
	{
		case WM_SYSKEYUP:
		case WM_KEYUP:
			{
				tab = wParam == 9;

				if(tab && ctrl && shift)
				{
					int iPage = TabCtrl_GetCurSel(m_hWnd) - 1;
					
					if(iPage == -1)
					{
						iPage = m_nextItem;
						while(m_tabs[iPage].get() == NULL)
						{
							iPage--;
						}
					}
					
					SwitchToTab(m_tabs[iPage]);
					return 0;
				}
				else if(tab && ctrl)
				{
					int iPage = TabCtrl_GetCurSel(m_hWnd) + 1;
					if(m_tabs[iPage].get() == NULL)
					{
						iPage = 0;
					}
					
					SwitchToTab(m_tabs[iPage]);
					return 0;
				}

				if(wParam == 17)
				{
					ctrl = false;
				}
				else if(wParam == 16)
				{
					shift = false;
				}
				return CallWindowProc(lpfnOldWndProc, hWnd, Message, wParam, lParam);
			}
			break;
		case WM_SYSKEYDOWN:
		case WM_KEYDOWN:
			{
				if(wParam == 17)
				{
					ctrl = true;
				}
				else if(wParam == 16)
				{
					shift = true;
				}

				return CallWindowProc(lpfnOldWndProc, hWnd, Message, wParam, lParam);
			}
			break;
		default:
			return CallWindowProc(lpfnOldWndProc, hWnd, Message, wParam, lParam);
	}
	return 0;
}